<?php

namespace common\service\project_warning_rules;

use support\ErrorCode;
use support\SingletonTrait;
use common\model\EntityModel;
use common\service\StatusService;
use common\service\EntityService;
use common\service\OptionsService;
use common\exception\LogicException;
use common\model\TaskRepairOrderModel;

/**
 * 剧集修改次数检查
 */
class EpisodeModifyTimeCheck implements ProjectWarningRuleInterface
{

    const CHECK_CODE = 'EPISODE_MODIFY_TIMES_CHECK';

    const NEED_PARAM = ['project_id'];

    const EPISODE_ASSET_TASK_REPAIR_ART_MAX_NUMBER = 'asset_task_repair_art_max_number';
    const EPISODE_SHOT_TASK_REPAIR_ART_MAX_NUMBER = 'shot_task_repair_art_max_number';

    const EPISODE_ASSET_TASK_REPAIR_QUALITY_MAX_NUMBER = 'asset_task_repair_quality_max_number';
    const EPISODE_SHOT_TASK_REPAIR_QUALITY_MAX_NUMBER = 'shot_task_repair_quality_max_number';

    const EPISODE_ENTITY_CLOSED_DESIGN_MAX_NUMBER = 'episode_entity_closed_design_max_number';
    const EPISODE_ENTITY_CLOSED_STORYBOARD_MAX_NUMBER = 'episode_entity_closed_storyboard_max_number';


    private array $errorMessage = [];


    use SingletonTrait;


    /**
     * 检查参数是否包含
     * @param $param
     * @return void
     */
    public function paramRequired($param)
    {
        foreach (self::NEED_PARAM as $paramKey) {
            if (!isset($param[$paramKey])) {
                throw new LogicException("param {$paramKey} needed", ErrorCode::DATA_INVALID);
            }
        }
    }

    /**
     * 检查是否满足
     * @param $checkData
     * @return bool
     * @throws \think\Exception
     */
    public function check($checkData)
    {
        $this->paramRequired($checkData);
        $projectId = $checkData['project_id'];
        $configs = $this->getLimitNumberConfig();

        // 查询出没有播放的剧集
        $notShowEpisodeList = EntityService::getInstance()->getNotShowEpisodeList($projectId);

        if (empty($notShowEpisodeList)) {
            return true;
        }
        $this->errorMessage = [];
        $this->repairCheck($configs, $notShowEpisodeList, $projectId);
        $this->entityClosedCheck($configs, $notShowEpisodeList, $projectId);

        return empty($this->errorMessage);
    }

    /**
     * 获取错误信息
     * @return array
     */
    public function getErrorMessage()
    {
        return $this->errorMessage;
    }

    /**
     * 查询返修次数阈值配置
     * @return array
     */
    private function getLimitNumberConfig()
    {
        $configItems = [
            self::EPISODE_ASSET_TASK_REPAIR_QUALITY_MAX_NUMBER,
            self::EPISODE_SHOT_TASK_REPAIR_QUALITY_MAX_NUMBER,
            self::EPISODE_ASSET_TASK_REPAIR_ART_MAX_NUMBER,
            self::EPISODE_SHOT_TASK_REPAIR_ART_MAX_NUMBER,
            self::EPISODE_ENTITY_CLOSED_DESIGN_MAX_NUMBER,
            self::EPISODE_ENTITY_CLOSED_STORYBOARD_MAX_NUMBER,
        ];
        return OptionsService::getInstance()->getOptionsConfigMultiItemData('project_warning_check', $configItems);
    }

    /**
     * 返修检测
     * @param $configs
     * @param $notShowEpisodeList
     * @param $projectId
     * @return void
     */
    private function repairCheck($configs, $notShowEpisodeList, $projectId)
    {
        $checkReportTypes = [];
        $having = [];
        $moduleIdList = [];
        $assetModuleId = module_by_code('asset')['id'];
        if (!empty($configs[self::EPISODE_ASSET_TASK_REPAIR_ART_MAX_NUMBER]) && intval($configs[self::EPISODE_ASSET_TASK_REPAIR_ART_MAX_NUMBER]) > 0) {
            $checkReportTypes['艺术返修'] = '艺术返修';
            $moduleIdList[$assetModuleId] = $assetModuleId;
            $having[] = "(epr.entity_module_id = {$assetModuleId} and tro.report_type = '艺术返修' and number > {$configs[self::EPISODE_ASSET_TASK_REPAIR_ART_MAX_NUMBER]})";
        }
        if (!empty($configs[self::EPISODE_ASSET_TASK_REPAIR_QUALITY_MAX_NUMBER]) && intval($configs[self::EPISODE_ASSET_TASK_REPAIR_ART_MAX_NUMBER]) > 0) {
            $checkReportTypes['质量返修'] = '质量返修';
            $moduleIdList[$assetModuleId] = $assetModuleId;
            $having[] = "(epr.entity_module_id = {$assetModuleId} and tro.report_type = '质量返修' and number > {$configs[self::EPISODE_ASSET_TASK_REPAIR_QUALITY_MAX_NUMBER]})";
        }

        $shotModuleId = module_by_code('shot')['id'];
        if (!empty($configs[self::EPISODE_SHOT_TASK_REPAIR_ART_MAX_NUMBER]) && intval($configs[self::EPISODE_SHOT_TASK_REPAIR_ART_MAX_NUMBER]) > 0) {
            $checkReportTypes['艺术返修'] = '艺术返修';
            $moduleIdList[$shotModuleId] = $shotModuleId;
            $having[] = "(epr.entity_module_id = {$shotModuleId} and tro.report_type = '艺术返修' and number > {$configs[self::EPISODE_SHOT_TASK_REPAIR_ART_MAX_NUMBER]})";
        }
        if (!empty($configs[self::EPISODE_SHOT_TASK_REPAIR_QUALITY_MAX_NUMBER]) && intval($configs[self::EPISODE_SHOT_TASK_REPAIR_QUALITY_MAX_NUMBER]) > 0) {
            $checkReportTypes['质量返修'] = '质量返修';
            $moduleIdList[$shotModuleId] = $shotModuleId;
            $having[] = "(epr.entity_module_id = {$shotModuleId} and tro.report_type = '质量返修' and number > {$configs[self::EPISODE_SHOT_TASK_REPAIR_QUALITY_MAX_NUMBER]})";
        }


        if (empty($checkReportTypes)) {
            return;
        }

        $notShowEpisodeListMap = array_column($notShowEpisodeList, 'name', 'id');
        $episodeIdS = implode(',', array_column($notShowEpisodeList, 'id'));
        $moduleIds = implode(',', $moduleIdList);
        $episodeCheckList = model(TaskRepairOrderModel::class)
            ->alias('tro')
            ->join('repair_order_affected_task toat on toat.task_repair_order_id = tro.id')
            ->join("(select distinct entity_id, episode_id, entity_module_id from entity_episode_relation where project_id = {$projectId} and episode_id in ({$episodeIdS}) and entity_module_id in ({$moduleIds})) epr on epr.entity_id = toat.entity_id")
            ->where([
                'tro.report_type' => ['IN', implode(',', $checkReportTypes)],
                'tro.project_id' => $projectId,
            ])
            ->having(implode(' OR ', $having))
            ->group('epr.episode_id, epr.entity_module_id, tro.report_type')
            ->field('epr.episode_id, epr.entity_module_id, count(tro.id) number, tro.report_type')
            ->select();

        foreach ($episodeCheckList as $episode) {
            $moduleName = '';
            $episodeName = $notShowEpisodeListMap[$episode['episode_id']];
            if ($episode['report_type'] === '艺术返修' && $episode['entity_module_id'] == $assetModuleId) {
                $limit = $configs[self::EPISODE_ASSET_TASK_REPAIR_ART_MAX_NUMBER];
                $moduleName = '资产';
            } elseif ($episode['report_type'] === '艺术返修' && $episode['entity_module_id'] == $shotModuleId) {
                $limit = $configs[self::EPISODE_SHOT_TASK_REPAIR_ART_MAX_NUMBER];
                $moduleName = '镜头';
            } elseif ($episode['report_type'] === '质量返修' && $episode['entity_module_id'] == $assetModuleId) {
                $limit = $configs[self::EPISODE_ASSET_TASK_REPAIR_QUALITY_MAX_NUMBER];
                $moduleName = '资产';
            } elseif ($episode['report_type'] === '质量返修' && $episode['entity_module_id'] == $shotModuleId) {
                $limit = $configs[self::EPISODE_SHOT_TASK_REPAIR_QUALITY_MAX_NUMBER];
                $moduleName = '镜头';
            } else {
                continue;
            }
            $limit = intval($limit);
            $this->errorMessage[] = $episodeName . $moduleName . '的' . $episode['report_type'] . '已超出' . ($episode['number'] - $limit) . '次';
        }
    }

    /**
     * 废除检查
     * @param array $configs
     * @param $notShowEpisodeList
     * @param $projectId
     * @return void
     * @throws \Exception
     */
    private function entityClosedCheck(array $configs, $notShowEpisodeList, $projectId)
    {
        $checkModuleId = [];
        $having = [];
        $designModuleId = module_by_code('design')['id'];
        if (!empty($configs[self::EPISODE_ENTITY_CLOSED_DESIGN_MAX_NUMBER]) && intval($configs[self::EPISODE_ENTITY_CLOSED_DESIGN_MAX_NUMBER]) > 0) {
            $checkModuleId[] = $designModuleId;
            $having[] = "(e.module_id = {$designModuleId} and number > {$configs[self::EPISODE_ENTITY_CLOSED_DESIGN_MAX_NUMBER]})";
        }
        $storyboardModuleId = module_by_code('storyboard')['id'];
        if (!empty($configs[self::EPISODE_ENTITY_CLOSED_STORYBOARD_MAX_NUMBER]) && intval($configs[self::EPISODE_ENTITY_CLOSED_STORYBOARD_MAX_NUMBER]) > 0) {
            $checkModuleId[] = $storyboardModuleId;
            $having[] = "(e.module_id = {$storyboardModuleId} and number > {$configs[self::EPISODE_ENTITY_CLOSED_STORYBOARD_MAX_NUMBER]})";
        }
        if (empty($checkModuleId)) {
            return;
        }
        $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
        $episodeIds = implode(',', array_column($notShowEpisodeList, 'id'));
        $moduleIdS = implode(',', $checkModuleId);
        $checkResults = model(EntityModel::class)->alias('e')
            ->join('module m on m.id = e.module_id')
            ->join("(select distinct episode_id,entity_id from entity_episode_relation where project_id = {$projectId} and episode_id in ({$episodeIds}) and entity_module_id in ({$moduleIdS})) epr on epr.entity_id = e.id")
            ->where(['e.project_id' => $projectId, 'e.module_id' => ['IN', $checkModuleId]])
            ->where("JSON_UNQUOTE(JSON_EXTRACT(e.json,concat('$.',m.code,'_status'))) = {$closedStatus['id']}")
            ->field('epr.episode_id,e.module_id,count(e.id) as number')
            ->group('epr.episode_id,e.module_id')
            ->having(implode(' OR ', $having))
            ->select();
        $notShowEpisodeListMap = array_column($notShowEpisodeList, 'name', 'id');
        foreach ($checkResults as $checkResult) {
            $episodeName = $notShowEpisodeListMap[$checkResult['episode_id']];
            if ($checkResult['module_id'] === $designModuleId) {
                $limit = $configs[self::EPISODE_ENTITY_CLOSED_DESIGN_MAX_NUMBER];
                $overflowName = '资产';
            } else if ($checkResult['module_id'] === $storyboardModuleId) {
                $limit = $configs[self::EPISODE_ENTITY_CLOSED_STORYBOARD_MAX_NUMBER];
                $overflowName = '镜头';
            } else {
                continue;
            }
            $limit = intval($limit);
            $this->errorMessage[] = $episodeName . $overflowName . '废除数量已超出' . ($checkResult['number'] - $limit) . '个';
        }
    }

    /**
     * @inheritDoc
     */
    public function getCheckCode()
    {
        return self::CHECK_CODE;
    }
}
